<?php

/* Definisce FBController, classe base di tutti i nostri controller
 * Inizializza automaticamente facebook, gestisce le opzioni di debug, etc.
 * Fornisce anche varie funzioni ausiliarie (protected)
 */



class FBController extends CI_Controller {
    function __construct()
    {
	parent::__construct();

	// Per le date, principalmente
	setlocale(LC_ALL, "it_IT.UTF-8");

	// Caricamento libreria Facebook presa da <http://brandonbeasley.com/blog/facebook-api-codeigniter/>
        $fblib_config['appId'] = $this->config->item('fb_appId');
        $fblib_config['secret'] = $this->config->item('fb_secret');
        $fblib_config['cookie'] = true;

        $this->load->library('facebook', $fblib_config);

	//Check to see if there is an active Facebook session.
	//We will not know if the session is valid until the call is made
	$fbsession = $this->facebook->getSession();

        //Check to see if the access_token is present in Facebook session data
        if (!empty($session['access_token'])) {
            //Save token to Session data for later use
            $this->session->set_userdata('access_token', $fbsession['access_token']);
	}

	// Ci assicuriamo di inviare dati UTF-8
	// In teoria dovremmo usare il tipo application/xhtml+xml, ma:
	//   - le vecchie versioni di IE non lo accettano,
	//   - qualunque errore nel documento diventerebbe fatale.
	// Quindi ci accontentiamo di inviare XHTML come text/html.
	// Nota: usare XHTML è necessario per inserire i tag <fb:login>.
	$this->output->set_header('Content-Type: text/html; charset=UTF-8');

	if (isset($_GET['debug']))
		$this->output->enable_profiler(TRUE);

	// A meno che non sia richiesto diversamente, impostiamo l'uso del motore
	// di rendering di IE9/ChromeFrame anche in intranet e simili.
	if (isset($_GET['ie'])) {
		if ($_GET['ie'] == 7)
			$this->output->set_header('X-UA-Compatible: IE=7');
		elseif ($_GET['ie'] == 8)
			$this->output->set_header('X-UA-Compatible: IE=8');
		elseif ($_GET['ie'] == 9)
			$this->output->set_header('X-UA-Compatible: IE=9');
	} else $this->output->set_header('X-UA-Compatible: IE=9,chrome=1');
    }

    protected function isLoggedIn()
    {
	// Restituisce TRUE se l'utente è noto
	// TODO: Serve controllare anche api("/me")?
	return ($this->facebook->getUser() != null);
    }

    protected function verifica_provenienza_ajax()
    {
	// Verifica che la richiesta sia stata inviata via AJAX
	// (e non, ad esempio, tramite form o simili, 
	// che sono facilmente usati per tentativi di Cross-Site Request Forgery)
	// 
	// Tutte le nostre operazioni che possono modificare dati richiedono 
	// un POST via AJAX.
	// E' una prima linea di difesa, bisogna anche inserire un token
	// per tutelarsi da vulnerabilità in flash e simili.

	if (!$this->input->is_ajax_request()) {
		log_message('error', 'Possibile tentativo di CSRF');
		show_error("Richiesta strana");
	}
    }

    protected function amici_noti()
    {
	// Restituisce la lista degli amici dell'utente corrente che ci hanno autorizzato

	// Mai cachare pagine che dipendono dagli amici di fb
	$this->output->set_header("Cache-Control: no-store, no-cache, must-revalidate");

	// Uso la vecchia API, farlo con FQL sembra sia molto lento e problematico
	return $this->facebook->api(array('method' => 'friends.getAppUsers'));
    }

    private function ricevi_eventi_fb()
    {
	// Restituisce gli eventi dell'utente e dei suoi amici su fb

	// Mai cachare pagine che dipendono dagli eventi di fb
	$this->output->set_header("Cache-Control: no-store, no-cache, must-revalidate");

	if (!$this->isLoggedIn())
		show_error("Effettuare il login per importare eventi", 401);


	// Era necessario questo troiaio o bastava chiamare normalmente l'api?
	// Probabilmente non lo sapremo mai
        define('FACEBOOK_APP_ID', $this->config->item('fb_appId'));
        define('FACEBOOK_SECRET', $this->config->item('fb_secret'));

        function get_facebook_cookie($app_id, $application_secret) {
          $args = array();
          parse_str(trim($_COOKIE['fbs_' . $app_id], '\\"'), $args);
          ksort($args);
          $payload = '';
          foreach ($args as $key => $value) {
            if ($key != 'sig') {
              $payload .= $key . '=' . $value;
            }
          }
          if (md5($payload . $application_secret) != $args['sig']) {
            return null;
          }
          return $args;
        }

        $this->cookie = get_facebook_cookie(FACEBOOK_APP_ID, FACEBOOK_SECRET);

        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL,'https://api.facebook.com/method/events.get?'.
                'format=json&'.
                'uid='.$this->cookie['uid'].'&'.
                'start_time='.time().'&'.
                'access_token='.$this->cookie['access_token']);

        curl_setopt($ch, CURLOPT_HEADER, false);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        $out = curl_exec($ch);
        curl_close($ch);

	/* Su altervista gli eid non si possono rappresentare come interi:
	 * ci assicuriamo che vengano trattati come stringhe (e non come float o simili)
	 * XXX: Ma perché prima funzionava?!? */
	$out = preg_replace('/"eid":(\d+)/', '"eid":"$1"', $out);
	return json_decode($out,true);
    }

    protected function eventi_fb_importabili($solo_conteggio = FALSE)
    {
	// Restituisce gli eventi visibili all'utente e che 
	// non sono già stati importati o nascosti

	$tutti = $this->ricevi_eventi_fb();
	if (($tutti == null) || (count($tutti) == 0)) {
		$ret = array("total_events" => 0, "my_events" => 0);
		if (!$solo_conteggio)
			$ret['events'] = null;
		return $ret;
	}

	// Costruisce la query per controllare gli eventi già importati (o che è stato richiesto di ignorare)
	// Ne approfittiamo anche per portare l'array degli eventi in forma associativa (eid => evento)
	$sql = "SELECT eid FROM "
	       . "((SELECT eid FROM `".$this->db->dbprefix('eventi')."` WHERE eid) "
	       . "UNION "
	       . "(SELECT eid FROM `".$this->db->dbprefix('eventi_fb_nascosti')."` WHERE fb_uid = " . $this->db->escape($this->facebook->getUser()) . ")) a "
	       . "WHERE eid=" . $tutti[0]['eid'];
	$eventi = array($tutti[0]['eid'] => $tutti[0]);
	for ($i = 1; $i < count($tutti); $i++) {
		$sql .= ' OR eid = ' . $tutti[$i]['eid'];
		$eventi[$tutti[$i]['eid']] = $tutti[$i];
	}

	// Otteniamo gli eid degli eventi indesiderati
	$query = $this->db->query($sql);
	$ris = $query->result_array();
	$eid_indesiderati = array(); // eid => true
	foreach($ris as $r)
		$eid_indesiderati[$r['eid']] = true;

	// Per differenza, otteniamo gli eventi desiderati
	$eventi = array_diff_key($eventi, $eid_indesiderati);

        // Conta gli eventi creati dall'utente loggato
	$created_by_me=0;
        foreach ($eventi as $e) {
	    if ($e['creator'] == $this->facebook->getUser())
		    $created_by_me++;
        }

        $ret = array("total_events" => count($eventi), "my_events" => $created_by_me);
	if (!$solo_conteggio)
		$ret["events"] = array_values($eventi);
	return $ret;
    }

    protected function timestamp_da_utente($s, $fuzzy, &$out_data_fine = null)
    {
	// Prova a interpretare una stringa inserita dall'utente come fosse una data
	// Restituisce un timestamp UNIX (o FALSE)
	
	if (strlen($s) < 3)
		return FALSE;

	$s = strtolower(str_replace('/','-',$s)); // Per PHP le barre indicano date in formato US

	// Interpreta "domani", giorni settimana e simili
	// (semplicemente traduce e usa strtotime, unico problematico: dopodomani)
	$trad = array('domani' => 'tomorrow', 'oggi' => 'today', 'ieri' => 'yesterday', 'dopodomani' => 'dopodomani',
		'lunedì' => 'next monday', 'martedì' => 'next tuesday', 'mercoledì' => 'next wednesday',
		'giovedì' => 'next thursday', 'venerdì' => 'next friday', 'sabato' => 'next saturday', 'domenica' => 'next sunday');
	if ($fuzzy)
		$giorno = $this->match_inizio($s, array_keys($trad));
	else $giorno = (array_key_exists($s, $trad) ? $s : FALSE);
	if ($giorno) {
		if ($giorno == 'dopodomani')
			return strtotime('tomorrow')+86400;
		else return strtotime($trad[$giorno]);
	}

	// Interpreta mesi come range
	if (func_num_args() == 3) {
		$mesi = array('gennaio','febbraio','marzo','aprile','maggio','giugno','luglio','agosto','settembre','ottobre','novembre','dicembre');
		if ($fuzzy)
			$m = $this->match_inizio($s, $mesi);
		else $m = array_search($s, $mesi);
		if ($m !== FALSE) {
			$m++;
			$out_data_fine = mktime(0,0,0,$m+1,1) - 86400;
			return mktime(0,0,0,$m,1);
		}
	}

	// Prova alcuni formati tipici italiani	
	if ((($t = strptime($s, '%d %B %Y')) || ($t = strptime($s, '%d-%m-%Y')))
		&& ($t['tm_year'] >= 111) && ($t = mktime(0,0,0,$t['tm_mon']+1,$t['tm_mday'], $t['tm_year']+1900)))
		return $t;
	if ((($t = strptime($s, '%d %B')) || ($t = strptime($s, '%d-%m')))
		&& ($t = mktime(0,0,0,$t['tm_mon']+1,$t['tm_mday'])))
		return $t;
	
	$this->load->helper('date');
	if ($t = human_to_unix($s)); // Prova il parser di CodeIgniter
		return $t;
	return strtotime($s); // Prova parse generico (usa nomi americani)
    }

    private function match_inizio($input_utente, $possibili)
    {
	// Autocomplete dei poveri
	if (!is_array($possibili))
		$possibili = array($possibili);

	for ($i = 0; $i < count($possibili); $i++)
		if (strncmp($input_utente, $possibili[$i], min(strlen($input_utente), strlen($possibili[$i]))) == 0)
			return $possibili[$i];
	return FALSE;
    }
}
